package com.mata.worktime.controller.dingtalk;

import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiUserGetRequest;
import com.dingtalk.api.request.OapiUserGetuserinfoRequest;
import com.dingtalk.api.request.OapiV2DepartmentListsubRequest;
import com.dingtalk.api.request.OapiV2UserListRequest;
import com.dingtalk.api.response.*;

import com.mata.worktime.configuration.dingtalk.AppConfig;
import com.mata.worktime.configuration.shiro.service.PasswordService;
import com.mata.worktime.controller.BaseController;
import com.mata.worktime.dto.dingtalk.ConfigDTO;
import com.mata.worktime.dto.dingtalk.ServiceResult;
import com.mata.worktime.dto.dingtalk.UserDTO;
import com.mata.worktime.entity.user.Dept;
import com.mata.worktime.entity.user.User;
import com.mata.worktime.exception.DingtalkEncryptException;
import com.mata.worktime.service.TokenService;
import com.mata.worktime.service.user.DeptService;
import com.mata.worktime.service.user.UserService;
import com.mata.worktime.util.dingtalk.JsApiSignature;
import com.mata.worktime.vo.ResultVO;
import com.taobao.api.ApiException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.mata.worktime.configuration.dingtalk.UrlConstant.*;


/**
 * 微应用QuickStart示例，免登功能
 */
@Controller
@RequestMapping("/dingtalk")
public class AuthController extends BaseController {
    private static final Logger log = LoggerFactory.getLogger(AuthController.class);

    private TokenService tokenService;
    private AppConfig appConfig;

    @Autowired
    public AuthController(
            TokenService tokenService,
            AppConfig appConfig
    ) {
        this.tokenService = tokenService;
        this.appConfig = appConfig;
    }

    @Autowired
    private DeptService deptService;

    @Autowired
    private UserService userService;

    @Autowired
    private PasswordService passwordService;


    /**
     * 钉钉用户登录，显示当前登录用户的userId和名称
     *
     * @param authCode 免登临时authCode
     * @return 当前用户
     */
    @PostMapping("/login")
    @ResponseBody
    public ServiceResult<UserDTO> login(@RequestParam String authCode) {

        String accessToken;

        // 获取accessToken
        ServiceResult<String> accessTokenSr = tokenService.getAccessToken();

        if (!accessTokenSr.isSuccess()) {
            return ServiceResult.failure(accessTokenSr.getCode(), accessTokenSr.getMessage());
        }
        accessToken = accessTokenSr.getResult();


        // 获取用户userId
        ServiceResult<String> userIdSr = getUserInfo(accessToken, authCode);
        if (!userIdSr.isSuccess()) {
            return ServiceResult.failure(userIdSr.getCode(), userIdSr.getMessage());
        }

        // 获取用户详情
        return getUser(accessToken, userIdSr.getResult());
    }

    /**
     * 访问/user/getuserinfo接口获取用户userId
     *
     * @param accessToken access_token
     * @param authCode    临时授权码
     * @return 用户userId或错误信息
     */
    private ServiceResult<String> getUserInfo(String accessToken, String authCode) {
        DingTalkClient client = new DefaultDingTalkClient(URL_GET_USER_INFO);
        OapiUserGetuserinfoRequest request = new OapiUserGetuserinfoRequest();
        request.setCode(authCode);
        request.setHttpMethod("GET");

        OapiUserGetuserinfoResponse response;
        try {
            response = client.execute(request, accessToken);
        } catch (ApiException e) {
            log.error("Failed to {}", URL_GET_USER_INFO, e);
            return ServiceResult.failure(e.getErrCode(), "Failed to getUserInfo: " + e.getErrMsg());
        }
        if (!response.isSuccess()) {
            return ServiceResult.failure(response.getErrorCode(), response.getErrmsg());
        }

        return ServiceResult.success(response.getUserid());
    }

    /**
     * 访问/user/get 获取用户名称
     *
     * @param accessToken access_token
     * @param userId      用户userId
     * @return 用户名称或错误信息
     */
    private ServiceResult<UserDTO> getUser(String accessToken, String userId) {
        DingTalkClient client = new DefaultDingTalkClient(URL_USER_GET);
        OapiUserGetRequest request = new OapiUserGetRequest();
        request.setUserid(userId);
        request.setHttpMethod("GET");

        OapiUserGetResponse response;
        try {
            response = client.execute(request, accessToken);
        } catch (ApiException e) {
            log.error("Failed to {}", URL_USER_GET, e);
            return ServiceResult.failure(e.getErrCode(), "Failed to getUserName: " + e.getErrMsg());
        }

        UserDTO user = new UserDTO();
        user.setName(response.getName());
        user.setUserid(response.getUserid());
        user.setAvatar(response.getAvatar());

        return ServiceResult.success(user);
    }

    @PostMapping("/config")
    public ServiceResult<ConfigDTO> config(@RequestParam String url) {
        System.out.println("url==================================================" + url);
        ConfigDTO config = new ConfigDTO();

        ServiceResult<String> jsTicketSr = tokenService.getJsTicket();
        if (!jsTicketSr.isSuccess()) {
            return ServiceResult.failure(jsTicketSr.getCode(), jsTicketSr.getMessage());
        }

        config.setAgentId(appConfig.getAgentId());
        config.setCorpId(appConfig.getCorpId());
        config.setJsticket(jsTicketSr.getResult());
        config.setNonceStr(JsApiSignature.genNonce());
        config.setTimeStamp(System.currentTimeMillis() / 1000);
        String sign;
        try {
            sign = JsApiSignature.sign(url, config.getNonceStr(), config.getTimeStamp(), config.getJsticket());
        } catch (DingtalkEncryptException e) {
            return ServiceResult.failure(e.getCode().toString(), e.getMessage());
        }
        config.setSignature(sign);
        return ServiceResult.success(config);
    }


    /**
     * v2 获取部门信息 deptId=1 为根部门
     */
    @GetMapping("/updateUser")
    @ResponseBody
    public ResultVO updateUser() {
        List<User> list = new ArrayList<>();
        Object obj = getDeptById(1L, new ArrayList<>()).getData();
        try {
            List<Dept> depts = objToList(obj, Dept.class);
            Dept adminDept= new Dept();
            adminDept.setId(1L);
            depts.add(adminDept);
            for (Dept dept : depts) {
                getUserByDept(dept.getId(), list);
            }
            if (list.size() > 0) {
                updateUser(list);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResultVO.success();
    }

    private void updateUser(List<User> list) {
        List<User> updateList = new ArrayList<>();
        List<User> insertList = new ArrayList<>();
        //ding user List
        List<String> dingUserList = list.stream().map(User::getId).collect(Collectors.toList());
        //my user List
        List<User> allUser = userService.getAllUser();

        if (Objects.nonNull(allUser) && allUser.size() > 0) {
            List<String> delUserList = new ArrayList<>();
            for (User user : allUser) {
                if (!dingUserList.contains(user.getId())) {
                    delUserList.add(user.getId()+"");
                }
            }
            //删除不是ding部门的
            if (delUserList.size() > 0) {
                String delStr = String.join(",", delUserList);

                userService.deleteUserByIds(delStr);
            }

            //重新查询数据库
            allUser = userService.getAllUser();

            //数据库已有deptIds
            List<String> userIds = allUser.stream().map(User::getId).collect(Collectors.toList());
            for (User user : list) {
                //deptId已存在则更新
                if (userIds.contains(user.getId())) {
                    updateList.add(user);
                } else {
                    insertList.add(user);
                }
            }
        } else {
            insertList.addAll(list);
        }

        if (updateList.size() > 0) {
            updateList.forEach(o->o.setPassword(null));
            updateList.forEach(o->o.setSalt(null));
            userService.saveOrUpdateBatch(updateList);
        }
        if (insertList.size() > 0) {
            userService.insertByList(insertList);
        }

    }

    /**
     * v2 获取部门信息 deptId=1 为根部门
     */
    @GetMapping("/updateDept")
    @ResponseBody
    public ResultVO updateDept() {
        List<Dept> list = new ArrayList<>();
        Object obj = getDeptById(1L, new ArrayList<>()).getData();
        try {
            list = objToList(obj, Dept.class);
            if (list.size() > 0) {
                updateDept(list);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResultVO.success();
    }

    private void updateDept(List<Dept> list) {
        List<Dept> updateList = new ArrayList<>();
        List<Dept> insertList = new ArrayList<>();
        //ding dept List
        List<Long> dingDeptList = list.stream().map(Dept::getId).collect(Collectors.toList());
        //my dept List
        List<Dept> allDept = deptService.getAllDept();

        if (Objects.nonNull(allDept) && allDept.size() > 0) {
            List<Long> delDeptList = new ArrayList<>();
            for (Dept dept : allDept) {
                if (!dingDeptList.contains(dept.getId())) {
                    delDeptList.add(dept.getId());
                }
            }
            //删除不是ding部门的
            if (delDeptList.size() > 0) {
                int del = deptService.delDeptByIds(delDeptList);
            }

            //重新查询数据库
            allDept = deptService.getAllDept();

            //数据库已有deptIds
            List<Long> deptIds = allDept.stream().map(Dept::getId).collect(Collectors.toList());
            for (Dept dept : list) {
                //deptId已存在则更新
                if (deptIds.contains(dept.getId())) {
                    updateList.add(dept);
                } else {
                    insertList.add(dept);
                }
            }
        } else {
            insertList.addAll(list);
        }

        if (updateList.size() > 0) {
            deptService.saveOrUpdateBatch(updateList);
        }
        if (insertList.size() > 0) {
            deptService.insertByList(insertList);
        }

    }

    private ResultVO getDeptById(Long deptId, List<Dept> results) {

        ServiceResult<String> accessTokenSr = tokenService.getAccessToken();
        if (!accessTokenSr.isSuccess()) {
            return ResultVO.error(Integer.valueOf(accessTokenSr.getCode()), accessTokenSr.getMessage());
        }
        DingTalkClient client = new DefaultDingTalkClient(v2_URL_DEPARTMENT_LIST);
        OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest();
        req.setDeptId(deptId);
        req.setHttpMethod("GET");

        OapiV2DepartmentListsubResponse rsp;
        try {
            rsp = client.execute(req, accessTokenSr.getResult());
        } catch (ApiException e) {
            log.error("Failed to {}", URL_DEPARTMENT_LIST, e);
            return ResultVO.error(Integer.valueOf(e.getErrCode()), "Failed to listDepartment: " + e.getErrMsg());
        }

        if (!rsp.isSuccess()) {
            return ResultVO.error(Integer.valueOf(rsp.getErrorCode()), "Failed to listDepartment: " + rsp.getErrmsg());
        }
        if (CollectionUtils.isNotEmpty(rsp.getResult())) {
            for (OapiV2DepartmentListsubResponse.DeptBaseResponse deptBaseResponse : rsp.getResult()) {
                Dept dept = new Dept();
                dept.setId(deptBaseResponse.getDeptId());
                dept.setDeptName(deptBaseResponse.getName());
                dept.setParentId(deptBaseResponse.getParentId());
                results.add(dept);
                getDeptById(dept.getId(), results);
            }
            return ResultVO.success(results);
        }
        return ResultVO.success(Collections.emptyList());
    }

    private ResultVO getUserByDept(Long deptId, List<User> users) {

        ServiceResult<String> accessTokenSr = tokenService.getAccessToken();
        if (!accessTokenSr.isSuccess()) {
            return ResultVO.error(Integer.valueOf(accessTokenSr.getCode()), accessTokenSr.getMessage());
        }
        DingTalkClient client = new DefaultDingTalkClient(v2_URL_USER_LIST);
        OapiV2UserListRequest req = new OapiV2UserListRequest();
        req.setDeptId(deptId);
        req.setCursor(0L);
        req.setSize(100L);
        req.setHttpMethod("GET");

        OapiV2UserListResponse rsp;
        try {
            rsp = client.execute(req, accessTokenSr.getResult());
        } catch (ApiException e) {
            log.error("Failed to {}", URL_DEPARTMENT_LIST, e);
            return ResultVO.error(Integer.valueOf(e.getErrCode()), "Failed to listDepartment: " + e.getErrMsg());
        }

        if (!rsp.isSuccess()) {
            return ResultVO.error(Integer.valueOf(rsp.getErrorCode()), "Failed to listDepartment: " + rsp.getErrmsg());
        }
        if (CollectionUtils.isNotEmpty(rsp.getResult().getList())) {
            for (OapiV2UserListResponse.ListUserResponse listUserResponse : rsp.getResult().getList()) {
                User user = new User();
                user.setId(listUserResponse.getUserid());
                user.setDeptId(listUserResponse.getDeptIdList().get(0) + "");
                user.setLoginName(listUserResponse.getMobile());
                user.setRemark(listUserResponse.getTitle());
                user.setPhonenumber(listUserResponse.getMobile());
                user.randomSalt();
                user.setPassword(passwordService.encryptPassword(user.getLoginName(), randomStr(),
                        user.getSalt()));
                user.setUserName(listUserResponse.getName());
                user.setEmail(listUserResponse.getEmail());
                users.add(user);

            }
            return ResultVO.success(users);
        }
        return ResultVO.success(Collections.emptyList());
    }

    private String randomStr(){
        String result = "";
        int  k = (int)((Math.random()*9+1)*100000);
        result = String.valueOf(k);
        return result;
    }
    /**
     * obj转list
     *
     * @param obj
     * @param cla
     * @param <T>
     * @return
     * @throws Exception
     */
    private <T> List<T> objToList(Object obj, Class<T> cla) throws Exception {
        List<T> list = new ArrayList<T>();
        if (obj instanceof ArrayList<?>) {
            for (Object o : (List<?>) obj) {
                list.add(cla.cast(o));
            }
            return list;
        }
        return null;
    }

    /**
     * list to string
     */
    private String listToString(List list, char separator) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < list.size(); i++) {
            if (i == list.size() - 1) {
                sb.append(list.get(i));
            } else {
                sb.append(list.get(i));
                sb.append(separator);
            }
        }
        return sb.toString();
    }

}
